<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
namespace WanZ\App\User\Listener;

use Hyperf\Consul\Exception\ServerException;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Framework\Event\MainWorkerStart;
use Hyperf\Server\Event\MainCoroutineServerStart;
use Hyperf\Framework\Event\BootApplication;
use Hyperf\Utils\Network;
use Hyperf\Consul\Agent;
use Hyperf\Guzzle\ClientFactory;
use Psr\Container\ContainerInterface;

class RegisterServiceListener implements ListenerInterface
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var StdoutLoggerInterface
     */
    protected $logger;

    /**
     * @var ConfigInterface
     */
    protected $config;

    /**
     * @var ClientFactory
     */
    protected $clientFactory;
   

    public function __construct(ContainerInterface $container)
    {
        $this->logger = $container->get(StdoutLoggerInterface::class);
        $this->config = $container->get(ConfigInterface::class);
        $this->clientFactory = $container->get(ClientFactory::class);
    }

    public function listen(): array
    {
        return [
            MainWorkerStart::class,
            MainCoroutineServerStart::class,
            // BootApplication::class,
        ];
    }

    /**
     * @param MainCoroutineServerStart|MainWorkerStart $event
     */
    public function process(object $event)
    {
        try {
            if ($this->config->get('services.enable.register') !== true) {
                throw new \InvalidArgumentException('not enable register');
            }
            
            $consulServer = $this->config->get('services.drivers.consul.uri');
            if (empty($consulServer)) {
                throw new \InvalidArgumentException('Invalid consul uri');
            }
            $token = $this->config->get('services.drivers.consul.token');
            if (empty($token)) {
                throw new \InvalidArgumentException('Invalid consul token');
            }            
            $clientFactory = $this->clientFactory;

            $agent = new Agent(function () use ($clientFactory, $consulServer, $token) {
                return $clientFactory->create([
                    'base_uri' => $consulServer,
                    'query' => [
                        'token' => $token
                    ]
                ]);
            });

            $name = 'userCenter2';
            $servers = $this->getServers();
            [$address, $port] = $servers['http'];
            $nextId = $name.'_'.$address;

            $requestBody = [];
            $requestBody['Name'] = $name;
            $requestBody['ID'] =  $nextId;
            $requestBody['Address'] = $address;
            $requestBody['Port'] = $port;
            $requestBody['Tags'] = ['mais828', 'php', 'hyperf', $name];
            $requestBody['EnableTagOverride'] = false;
            $requestBody['Weights'] = [
                'Passing' => 10,
                'Warning' => 1
            ];
            $requestBody['Check'] = [
                'HTTP' => 'http://' . $address . ':' . $port . '/user/status',
                'Interval' => $this->config->get('services.drivers.consul.check.interval', '1s'),
                'Timeout' => '1s',
                'DeregisterCriticalServiceAfter' => $this->config->get('services.drivers.consul.check.deregister_critical_service_after', '90m'),
            ];
            $response = $agent->registerService($requestBody);
            if ($response->getStatusCode() === 200) {
                $this->logger->info(sprintf('Service %s:%s register to the consul successfully.', $name, $nextId));
            } else {
                $this->logger->warning(sprintf('Service %s register to the consul failed.', $name));
            }
        } catch (ServerException $throwable) {
            if (strpos($throwable->getMessage(), 'Connection failed') !== false) {
                $this->logger->warning('Cannot register service, connection of service center failed, re-register after 10 seconds.');
                sleep(10);
            } else {
                throw $throwable;
            }
        }
    }

    protected function getServers(): array
    {
        $result = [];
        $servers = $this->config->get('server.servers', []);
        foreach ($servers as $server) {
            if (! isset($server['name'], $server['host'], $server['port'])) {
                continue;
            }
            if (! $server['name']) {
                throw new \InvalidArgumentException('Invalid server name');
            }
            $host = $server['host'];
            if (in_array($host, ['0.0.0.0', 'localhost'])) {
                $host = Network::ip();
            }
            if (! filter_var($host, FILTER_VALIDATE_IP)) {
                throw new \InvalidArgumentException(sprintf('Invalid host %s', $host));
            }
            $port = $server['port'];
            if (! is_numeric($port) || ($port < 0 || $port > 65535)) {
                throw new \InvalidArgumentException(sprintf('Invalid port %s', $port));
            }
            $port = (int) $port;
            $result[$server['name']] = [$host, $port];
        }
        return $result;
    }

}
